const merge = require('deepmerge');

const MERGE = 'merge';
const REPLACE = 'replace';
const COMBINE = 'combine';

const arrayMerge = (target, source, options) => {
  const destination = target.slice();
  const { arrayMode } = options;

  if (arrayMode === REPLACE) {
    return source;
  }
  if (arrayMode === COMBINE) {
    source.forEach((item, index) => {
      if (typeof destination[index] === 'undefined') {
        destination[index] = options.cloneUnlessOtherwiseSpecified(item, options);
      } else if (options.isMergeableObject(item)) {
        destination[index] = merge(target[index], item, options);
      } else if (target.indexOf(item) === -1) {
        destination.push(item);
      }
    });
  } else {
    source.forEach((item, index) => {
      if (typeof destination[index] === 'undefined') {
        destination[index] = options.cloneUnlessOtherwiseSpecified(item, options);
      } else {
        if (options.isMergeableObject(item)) {
          destination[index] = merge(target[index], item, options);
        } else {
          destination[index] = item;
        }
      }
    });
  }
  return destination;
};

/**
 *
 *
 * @property {string} MERGE
 * @property {string} REPLACE
 * @property {string} COMBINE
 * @param options
 * @returns {*|{}}
 */
const mergeOptions = (options) => {
  options = options || {};

  let { arrayMode } = options;
  if (typeof arrayMode === 'undefined' || arrayMode === null) {
    arrayMode = MERGE;
  }
  options.arrayMode = arrayMode;

  if (typeof options.arrayMerge !== 'function') {
    options.arrayMerge = arrayMerge;
  }

  return options;
};

Object.defineProperties(mergeOptions, {
  MERGE  : {
    value: MERGE,
  },
  REPLACE: {
    value: REPLACE,
  },
  COMBINE: {
    value: COMBINE,
  },
});

const deepMerge = (objects, options = { arrayMode: MERGE }) => {
  return merge.all(objects, mergeOptions(options));
};

module.exports = deepMerge;